package cgs_map

import (
	"cgs-server/helper"
	"cgs-server/server"
	"cgs-server/server/assets/mesh"
	"cgs-server/server/base"
	"cgs-server/server/model"
	"errors"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"io/ioutil"
	"path/filepath"
	"strings"
)

type MapService interface {
	base.Service

	Export(id primitive.ObjectID) ([]*model.ZipFileData, error)
	Import(data []*model.ZipFileData, user *model.User) (string, error)
}

func NewMapService() MapService {
	repo := NewMapRepository()
	return &mapService{
		ServiceImpl: base.ServiceImpl{Repo: repo},
		mapRepo:     repo,
	}
}

type mapService struct {
	base.ServiceImpl
	mapRepo MapRepository
}

func (m *mapService) Import(data []*model.ZipFileData, user *model.User) (string, error) {
	sceneID := primitive.NewObjectID()
	var err error
	for _, value := range data {
		if strings.HasSuffix(value.Name, "scene.json") {
			var cgsMap CgsMap
			if err := helper.FromJSON(value.Body, &cgsMap); err != nil {
				return "", err
			}
			cgsMap.SetId(sceneID)
			//cgsMap.Name += "_copy"
			cgsMap.TenantId = user.GetTenantId()

			if cgsMap.Data != nil {
				if sceneData, ok := cgsMap.Data.([]interface{}); ok {
					// 处理表单和模型
					meshDoc := bson.M{}
					for _, modelData := range data {
						if strings.Contains(modelData.Name, "model") {
							name := filepath.Base(modelData.Name)
							file := mesh.ZipFile{
								Filename: name,
								Size:     int64(len(modelData.Body)),
								FileType: "application/x-zip-compressed",
								Content:  modelData.Body,
							}
							meshService := mesh.NewMeshService()
							meshDoc, err = meshService.UploadMesh(&file, user)
							if err != nil {
								return "", err
							}
							//earthMap["url"] = doc["Url"]
							//earthMap["modelId"] = doc["_id"].(primitive.ObjectID).Hex()
						}
					}
					if savePath, ok := meshDoc["SavePath"].(string); ok {
						// 查找模型
						for _, value := range sceneData {
							data := value.(map[string]interface{})
							if data["type"] != "style" {
								continue
							}
							if _, ok := data["data"].(string); ok {
								break
							}

							dta := data["data"].(map[string]interface{})
							sources := dta["sources"].(map[string]interface{})
							for _, source := range sources {
								s := source.(map[string]interface{})
								sourceType := s["type"].(string)
								if sourceType == "raster" {
									continue
								}
								if s["data"] == nil {
									continue
								}

								d := s["data"].(string)
								name := filepath.Base(d)
								s["data"] = savePath + "/data/" + name
							}
						}
					}
				}
			}

			// 保存data数据
			mapDataID := primitive.NewObjectID()
			cgsMapData := CgsMapData{
				ID:   mapDataID,
				Data: cgsMap.Data,
			}

			err = m.mapRepo.SaveData(&cgsMapData)
			if err != nil {
				return "", err
			}

			cgsMap.DataID = mapDataID
			err = m.mapRepo.Insert(&model.DataUserModel{
				Data: &cgsMap,
				User: user,
			})

			if err != nil {
				return "", err
			}
		}
	}
	return sceneID.Hex(), nil
}

func (m *mapService) Export(id primitive.ObjectID) ([]*model.ZipFileData, error) {
	//压缩
	var zipData []*model.ZipFileData
	//获取projectID
	projectId, _ := m.mapRepo.FindByProjectId(id)

	sceneID, err := projectId.GetSceneId()
	if err != nil {
		return nil, err
	}
	sceneId, _ := primitive.ObjectIDFromHex(sceneID)

	//sceneJson数据
	scene, _ := m.mapRepo.FindBySceneId(sceneId)
	res, err := helper.ToJSON(scene)
	if err != nil {
		return nil, err
	}

	//将数据写入文件
	if len(res) > 0 {
		zipData = append(zipData, &model.ZipFileData{
			Name: server.ExportEditorFile + "scene.json",
			Body: res,
		})
	} else {
		return zipData, nil
	}

	if scene.Data == nil {
		return zipData, nil
	}

	if _, ok := scene.Data.(string); ok {
		return zipData, nil
	}

	sceneData := scene.Data.(bson.A)

	// 查找模型
	modelUrl := ""
	modelName := ""
	for _, value := range sceneData {
		data := value.(bson.D).Map()
		if data["type"] != "style" {
			continue
		}

		if _, ok := data["data"].(string); ok {
			break
		}

		dta := data["data"].(bson.D).Map()
		sources := dta["sources"].(bson.D).Map()
		for _, source := range sources {
			s := source.(bson.D).Map()
			sourceType := s["type"].(string)
			if sourceType == "raster" {
				continue
			}
			if s["data"] == nil {
				continue
			}

			d := s["data"].(string)
			if d != "" && strings.HasPrefix(d, "/Upload/") {
				n := strings.SplitN(d, "/", 5)
				newUrl := fmt.Sprintf("%s/temp", strings.Join(n[:len(n)-1], "/"))

				dir, err := ioutil.ReadDir(server.MapPath(newUrl))
				if err != nil {
					return nil, err
				}
				if len(dir) == 0 {
					break
				}

				modelName = dir[0].Name()
				modelUrl = fmt.Sprintf("%s/%s", newUrl, modelName)
			}
		}
	}

	if modelUrl != "" {
		jsonFile, err := ioutil.ReadFile(server.MapPath(modelUrl))
		if err != nil {
			return nil, err
		}

		if len(jsonFile) > 0 {
			zipData = append(zipData, &model.ZipFileData{
				Name: server.ExportEditorFile + "model/" + modelName,
				Body: jsonFile,
			})
		}
	}

	return zipData, nil
}

func (m *mapService) List(mdl *model.DataUserModel) (interface{}, error) {
	if _, ok := mdl.Data.(*CgsMap); ok {
		// 查找列表
		results, err := m.Repo.Find(mdl)
		if err != nil {
			return nil, err
		}
		// 计数
		mdl.Filter = &bson.M{"isDeleted": 0}
		count, err := m.Repo.Count(mdl)

		res := &model.PageResult{
			Records: results,
			Total:   count,
			Size:    mdl.Page.Size,
		}

		return res, err
	} else {
		return nil, model.NewTypeError("VisualMap")
	}
}

func (m *mapService) Save(mdl *model.DataUserModel) error {
	cgsMap, ok := mdl.Data.(*CgsMap)
	if !ok {
		return model.NewTypeError("VisualMap")
	}

	mapData := &CgsMapData{
		ID:   primitive.NewObjectID(),
		Data: cgsMap.Data,
	}
	err := m.mapRepo.SaveData(mapData)
	if err != nil {
		return err
	}
	cgsMap.DataID = mapData.ID

	if cgsMap.Id.IsZero() {
		err = m.mapRepo.Insert(mdl)
		return err
	} else {
		one, err := m.mapRepo.FindOne(mdl)
		if err != nil {
			return err
		}

		cMap, ok := one.(*CgsMap)
		if !ok {
			return errors.New("cgsMap type error")
		}
		err = m.mapRepo.RemoveData(cMap.DataID)
		if err != nil {
			return err
		}

		err = m.mapRepo.Update(mdl)
		return err
	}
}

func (m *mapService) GetOne(mdl *model.DataUserModel) (interface{}, error) {
	one, err := m.mapRepo.FindOne(mdl)
	if err != nil {
		return nil, err
	}

	cMap, ok := one.(*CgsMap)
	if !ok {
		return nil, errors.New("cgsMap type error")
	}

	mapData, err := m.mapRepo.GetData(cMap.DataID)
	if err == nil && mapData != nil {
		cMap.Data = mapData.Data
	} else {
		cMap.Data = []bson.M{}
	}

	return cMap, nil

}
